<template>
  <div class="annotation-box" id="annotation_box">
    <div class="img-wrapper" ref="wrapperRef">
      <div
        :style="{
          transform: `translate(${translateX}px, ${translateY}px)`
        }"
        ref="containerRef"
      >
        <img
          ref="imgRef"
          class="paper-img"
          :draggable="false"
          alt="答案图片"
          @load="onImgLoad"
          crossOrigin="anonymous"
          :src="url"
          :style="{
            transform: `rotate(${rotateDeg}deg) scale(${scaleRate})`
          }"
        />
      </div>
    </div>
    <div
      class="canvas-box"
      :style="{
        transform: `scale(${scaleRate})`
      }"
    >
      <canvas id="fabric-canvas-id" ref="canvasRef" width="0" height="0" />
    </div>
  </div>
</template>

<script>
import { ref, onMounted, watch, toRefs } from 'vue'
import { fabric } from 'fabric-with-erasing'
import { annotateMitt } from '@/utils/tools/bus'
import rightSvg from '@/assets/images/assignment/mark/right.png'
import halfSvg from '@/assets/images/assignment/mark/half.png'
import wrongSvg from '@/assets/images/assignment/mark/wrong.png'
import html2Canvas from 'html2canvas'
import { uploadImg } from '@/utils'
import { ElMessage, ElLoading } from 'element-plus'
import penSvg from '@/assets/images/assignment/mark/draw-pen.png'
import eraser1 from '@/assets/images/assignment/mark/eraser-1.png'
import eraser2 from '@/assets/images/assignment/mark/eraser-2.png'
import eraser3 from '@/assets/images/assignment/mark/eraser-3.png'

const EraserArr = [eraser1, eraser2, eraser3]
export default {
  name: 'annotationBox',
  props: {
    oldUrl: {
      default: ''
    },
    imgUrl: {
      default: ''
    },
    actUrl: {
      default: ''
    }
  },

  setup(props) {
    const { oldUrl, imgUrl, actUrl } = toRefs(props)
    let canvasFabric

    const FABRIC_CANVAS_ID = 'fabric-canvas-id'
    const url = ref('')
    // 监听单个数据源
    watch(
      [imgUrl, oldUrl, actUrl],
      () => {
        url.value = actUrl.value || oldUrl.value || imgUrl.value
      },
      {
        immediate: true,
        deep: true
      }
    )

    const containerRef = ref()
    const imgRef = ref(null)

    const translateX = ref(0)
    const translateY = ref(0)
    /* 缩放与拖动 */
    const scaleRate = ref(1)
    const bigger = () => {
      if (scaleRate.value >= 2) {
        scaleRate.value = 2
        return
      }
      scaleRate.value = Number((scaleRate.value + 0.2).toFixed(1))
    }
    const smaller = () => {
      if (scaleRate.value <= 0.2) {
        scaleRate.value = 0.2
        return
      }
      scaleRate.value = Number((scaleRate.value - 0.2).toFixed(1))
    }

    /* 旋转 */
    const rotateDeg = ref(0)
    const rotate = () => {
      rotateDeg.value -= 180
    }

    /**
     * 初始化原图片
     */
    let count = 0
    const onImgLoad = () => {
      if (count) return
      count++
      const loading = ElLoading.service({
        lock: true,
        text: '加载中',
        background: 'rgba(0, 0, 0, 0.4)',
        target: document.getElementById('annotation_box')
      })
      initCanvas()
      loading.close()
    }
    /**
     * 初始化画布
     */
    const initCanvas = () => {
      canvasFabric = new fabric.Canvas(FABRIC_CANVAS_ID, {
        width: imgRef.value.width, // 画布宽度
        height: imgRef.value.height, // 画布高度
        crossOrigin: 'anonymous'
      })
      penModel()
      handleDeleteControl()
    }

    const getIconWidth = () => {
      const RATIO = 20 // 根据 canvas 的宽度确定图像的宽度，假设按照 1/20 比例缩放
      const MIN_WIDTH = 25 // 限制图像宽度在 20 到 60 的范围内
      const MAX_WIDTH = 60
      const canvasWidth = canvasFabric.getWidth()
      let iconWidth = canvasWidth / RATIO
      iconWidth = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, iconWidth))
      return iconWidth
    }

    // 暂停所有的绘制
    const stopModel = () => {
      canvasFabric.isDrawingMode = false // 停止绘画
      canvasFabric.freeDrawingCursor = 'default'
      canvasFabric.off('mouse:down') // 关闭✅/❌的监听，输入框的定位
      canvasFabric.discardActiveObject() // 将当前活跃的对象设为不活跃
      const allTextbox = canvasFabric
        .getObjects()
        .filter(object => object.type === 'textbox')
      allTextbox.forEach(textbox => {
        if (textbox.type === 'textbox' && !textbox.text.trim()) {
          canvasFabric.remove(textbox)
        }
      })
      canvasFabric.requestRenderAll()
    }

    /**
     * ===批改
     */
    const maskModel = type => {
      stopModel()
      let svg = null
      switch (type) {
        case 'right':
          svg = rightSvg
          break
        case 'half':
          svg = halfSvg
          break
        default:
          svg = wrongSvg
      }
      canvasFabric.selection = false
      canvasFabric.on('mouse:down', options => {
        // 检查是否点击在空白区域
        if (options.target === null) {
          fabric.Image.fromURL(
            svg,
            img => {
              img.scaleToWidth(getIconWidth())
              img.set({
                left: options.e.offsetX - img.getScaledWidth() / 2, // 使图片中心在点击点
                top: options.e.offsetY - img.getScaledHeight() / 2,
                angle: rotateDeg.value / 360
              })
              canvasFabric.add(img) // 添加图片到画布
              canvasFabric.renderAll() // 更新画布显示
            },
            {
              crossOrigin: 'anonymous'
            }
          )
        }
      })
    }

    /**
     * ===画笔
     */
    const penBrushWidth = ref(3)
    const penModel = () => {
      stopModel()
      canvasFabric.isDrawingMode = true
      canvasFabric.freeDrawingBrush = new fabric.PencilBrush(canvasFabric)
      canvasFabric.freeDrawingBrush.color = 'red' // 画笔颜色
      canvasFabric.freeDrawingBrush.width = penBrushWidth.value // 画笔宽度
      canvasFabric.freeDrawingCursor = `url(${penSvg}) 0 20, auto`
    }
    /**
     * ===文本框
     */
    const wordModel = () => {
      stopModel()
      canvasFabric.on('mouse:down', options => {
        if (options.target && options.target.type === 'textbox') {
          // 如果已经是一个 Textbox 对象，则进入编辑模式
          return
        }
        if (options.target !== null) {
          // 点击到了其他对象的时候，也要把上一个空的textbox清掉
          return
        }
        // 获取鼠标点击位置
        const pointer = canvasFabric.getPointer(options.e)
        // 创建一个新的 Textbox 对象并添加到canvas上
        const textbox = new fabric.Textbox('', {
          left: pointer.x,
          top: pointer.y,
          width: 120 * scaleRate.value, // 初始宽度
          fontSize: 16,
          fill: 'red',
          borderColor: 'red', // 边框颜色
          padding: 2,
          editingBorderColor: 'red',
          hasControls: true
        })
        canvasFabric.add(textbox)
        // 让 Textbox 进入编辑状态
        canvasFabric.setActiveObject(textbox)
        textbox.enterEditing()
      })
    }

    const eraserBrushWidth = ref(1)

    /**
     * ===橡皮擦
     */
    const cleanModel = () => {
      stopModel()
      const eraserBrush = new fabric.EraserBrush(canvasFabric)
      eraserBrush.width = eraserBrushWidth.value * 18
      canvasFabric.freeDrawingBrush = eraserBrush
      canvasFabric.isDrawingMode = true
      let png = EraserArr[eraserBrushWidth.value - 1]
      canvasFabric.freeDrawingCursor = `url(${png}) 20 30, auto`
    }

    /**
     * ===清除画布
     */
    const clearModel = () => {
      canvasFabric.clear()
      stopModel()
    }

    /**
     * 移动画布
     */
    const moveMode = () => {
      if (!imgRef.value) {
        return
      }
      document.onkeydown = function (e) {
        if (e.keyCode < 37 || e.keyCode > 40) return
        e.preventDefault()
        let { left, top, bottom } = imgRef.value.getBoundingClientRect()

        let {
          right: wrapperRight,
          top: wrapperTop,
          bottom: wrapperBottom
        } = imgRef.value.parentElement.getBoundingClientRect()
        const step = 20
        const edge = 30
        switch (e.keyCode) {
          case 37:
            if (left < edge) return
            translateX.value = translateX.value - step
            break
          case 38:
            if (bottom - wrapperTop < edge) return
            translateY.value = translateY.value - step
            break
          case 39:
            if (wrapperRight - left < edge) return
            translateX.value = translateX.value + step
            break
          case 40:
            if (wrapperBottom - top < edge) return
            translateY.value = translateY.value + step
            break
        }
      }
    }

    const onSave = async () => {
      if (!imgRef.value) return
      const node = imgRef.value.parentElement.parentElement.parentElement

      const canvas = await html2Canvas(node, {
        backgroundColor: null,
        allowTaint: true,
        useCORS: true,
        scale: 3,
        width: imgRef.value.width - 2, // 画布宽度
        height: imgRef.value.height // 画布高度
      })

      const loading = ElLoading.service({
        lock: true,
        text: '加载中',
        background: 'rgba(0, 0, 0, 0.4)',
        target: document.getElementById('annotation_box')
      })
      // 创建一个新的画布用于存放裁剪结果
      const blob = await new Promise(resolve => {
        canvas.toBlob(resolve, 'image/jpeg', 0.8)
      })
      let file = new File(
        [blob],
        `web_annotation_${new Date().getTime()}.jpg`,
        {
          type: 'image/jpeg'
        }
      )

      let res = await uploadImg(file)
      loading.close()

      if (res.res.status === 200) {
        ElMessage.success('批注成功！')
        return res.url
      }

      ElMessage.error('保存失败，请重试')
      return ''
    }

    // 重写插件删除样式
    const handleDeleteControl = () => {
      const setControlVisibility = (object, canvas) => {
        object.setControlsVisibility({
          tl: false, // 禁用其他控制点
          tr: false,
          br: false,
          bl: false,
          ml: false,
          mt: false,
          mr: false,
          mb: false,
          mtr: false,
          deleteControl: true // 启用自定义删除控制点
        })
        canvas.requestRenderAll() // 更新画布以应用更改
      }

      // 自定义控件图标的绘制方法
      const renderDeleteIcon = (ctx, left, top) => {
        const size = 20 // 控件尺寸

        ctx.save()
        ctx.translate(left, top)

        // 绘制删除按钮
        ctx.beginPath()
        ctx.arc(0, 0, size / 2, 0, Math.PI * 2, false)
        ctx.fillStyle = 'rgba(29, 35, 49, 0.5)' // 删除按钮颜色
        ctx.fill()

        ctx.beginPath()
        ctx.moveTo(-size / 4, -size / 4)
        ctx.lineTo(size / 4, size / 4)

        ctx.moveTo(size / 4, -size / 4)
        ctx.lineTo(-size / 4, size / 4)

        ctx.strokeStyle = 'white' // X符号颜色
        ctx.lineWidth = 2
        ctx.stroke()

        ctx.restore()
      }

      const deleteControl = new fabric.Control({
        x: 0.5,
        y: -0.5,
        offsetX: 4,
        offsetY: -6,
        cursorStyle: 'pointer',
        mouseUpHandler: function (eventData, transform) {
          const target = transform.target
          const canvas = target.canvas
          canvas.remove(target) // 从画布中移除对象
          canvas.requestRenderAll()
        },
        render: renderDeleteIcon,
        cornerSize: 24
      })
      canvasFabric.on('object:added', e => {
        const object = e.target
        if (object) {
          object.controls.deleteControl = deleteControl
          setControlVisibility(object, canvasFabric)
        }
      })

      canvasFabric.on('selection:created', e => {
        setControlVisibility(e.selected[0], canvasFabric)
      })
    }

    // 测试删除功能
    // const test = () => {
    //   canvasFabric = new fabric.Canvas(
    //     document.getElementById(FABRIC_CANVAS_ID),
    //     {
    //       width: 800,
    //       height: 600
    //     }
    //   )

    //   // 添加一个矩形对象来测试
    //   const rect = new fabric.Rect({
    //     left: 100,
    //     top: 100,
    //     fill: 'red',
    //     width: 150,
    //     height: 100,
    //     angle: 30
    //   })
    //   canvasFabric.add(rect)
    // }

    onMounted(() => {
      annotateMitt.on('annotationControl', param => {
        const { model } = param
        switch (model) {
          case 'bigger':
            stopModel()
            bigger()
            break
          case 'smaller':
            stopModel()
            smaller()
            break
          case 'rotate':
            stopModel()
            rotate()
            break
          case 'right':
            maskModel('right')
            break
          case 'half':
            maskModel('half')
            break
          case 'wrong':
            maskModel('wrong')
            break
          case 'pen':
            penModel()
            break
          case 'text':
            wordModel()
            break
          case 'clean':
            cleanModel()
            break
          case 'cleanAll':
            clearModel()
            break
          case 'stop':
            stopModel()
            break
          case 'save-img':
            stopModel()
            scaleRate.value = 1
            rotateDeg.value = 0
            setTimeout(async () => {
              let url = await onSave()
              if (url) {
                annotateMitt.emit('annotationControl', {
                  model: 'save-img-url',
                  url
                })
              }
            }, 200)
            break
          case 'pen-size':
            penBrushWidth.value = param.penSize
            penModel()
            break
          case 'clean-size':
            eraserBrushWidth.value = param.penSize
            cleanModel()
            break
          case 'resetUrl':
            clearModel()
            url.value = props.imgUrl
            console.log('resetUrl')

            break
        }
      })
      moveMode()
    })
    return {
      url,
      translateX,
      translateY,
      scaleRate,
      rotateDeg,
      imgRef,
      onImgLoad
    }
  }
}
</script>

<style scoped lang="less">
.annotation-box {
  position: relative;
  overflow: hidden;
  z-index: 1;
  width: 100%;
  min-height: 400px;
  .img-wrapper {
    width: 100%;
    height: 100%;
    overflow: hidden;
    .paper-img {
      height: 100%;
      width: 100%;
    }
  }
  .canvas-box {
    position: absolute;
    left: 0;
    top: 0;
  }
}
.cursor-1 {
  cursor: grab;
}
</style>
